 
grammar SOL_Functional;

tokens { INDENT, DEDENT }

@lexer::header {
  import com.yuvalshavit.antlr4.DenterHelper;
}

@lexer::members {
  private final DenterHelper denter = new DenterHelper(NEWLINE,
                                                       SOL_FunctionalParser.INDENT,
                                                       SOL_FunctionalParser.DEDENT)
  {
    @Override
    public Token pullToken() {
      return SOL_FunctionalLexer.super.nextToken();
    }
  };

  @Override
  public Token nextToken() {
    return denter.nextToken();
  }
}

functional_section : (protocol_definition) (funcdef)* (setup_stats) (run_stats) EOF;

protocol_definition : PROTOCOL_KEYWORD NAME  ':' NEWLINE;
setup_stats : SETUP_KEYWORD  ':' INDENT+ file_input ;
run_stats : RUN_KEYWORD  ':' INDENT+ file_input ;


single_input: NEWLINE | simple_stmt | compound_stmt NEWLINE;
file_input: (NEWLINE | stmt)* (NEWLINE | DEDENT | ENDMARKER);
eval_input: testlist NEWLINE* ;//ENDMARKER;

decorator: '@' dotted_name ( '(' (arglist)? ')' )? NEWLINE;
decorators: decorator+;
decorated: decorators (classdef | funcdef);
funcdef: 'def' NAME parameters ('->' test)? ':' suite;
parameters: '(' (typedargslist| strongtypedargslist)? ')';
strongtypedargslist : (stfpdef ('=' test)? (',' stfpdef ('=' test)?)* (','
       ('*' (stfpdef)? (',' stfpdef ('=' test)?)* (',' '**' stfpdef)? | '**' stfpdef)?)?
     |  '*' (stfpdef)? (',' stfpdef ('=' test)?)* (',' '**' stfpdef)? | '**' stfpdef);
stfpdef: DATATYPE NAME (':' test)?;
typedargslist: (tfpdef ('=' test)? (',' tfpdef ('=' test)?)* (','
       ('*' (tfpdef)? (',' tfpdef ('=' test)?)* (',' '**' tfpdef)? | '**' tfpdef)?)?
     |  '*' (tfpdef)? (',' tfpdef ('=' test)?)* (',' '**' tfpdef)? | '**' tfpdef);
tfpdef: NAME (':' test)?;
varargslist: (vfpdef ('=' test)? (',' vfpdef ('=' test)?)* (','
       ('*' (vfpdef)? (',' vfpdef ('=' test)?)* (',' '**' vfpdef)? | '**' vfpdef)?)?
     |  '*' (vfpdef)? (',' vfpdef ('=' test)?)* (',' '**' vfpdef)? | '**' vfpdef);
vfpdef: NAME;

suite: NEWLINE* simple_stmt |   INDENT* stmt+ DEDENT*;    //suite		: simple_stmt| NEWLINE INDENT ( stmt )+ DEDENT;
stmt: simple_stmt | compound_stmt | map_stmt | join_stmt;


//simple_stmt: small_stmt (';' small_stmt)* (';')? NEWLINE;
simple_stmt: small_stmt (SEMI_COLON small_stmt)* (SEMI_COLON)? NEWLINE;
small_stmt: (expr_stmt | NAME ':' expr_stmt | del_stmt | pass_stmt | flow_stmt |
             import_stmt | global_stmt | nonlocal_stmt | assert_stmt);
expr_stmt: testlist_star_expr (augassign (yield_expr|testlist) |
                     ('=' (yield_expr|testlist_star_expr))*);
testlist_star_expr: (test|star_expr ) (',' (test|star_expr))* (',')?;
augassign: ('+=' | '-=' | '*=' | '/=' | '%=' | '&=' | '|=' | '^=' |
            '<<=' | '>>=' | '**=' | '//=');

del_stmt: 'del' exprlist;
pass_stmt: 'pass';
flow_stmt: break_stmt | continue_stmt | return_stmt | raise_stmt | yield_stmt;
break_stmt: 'break';
continue_stmt: 'continue';
return_stmt: 'return' (testlist)?;
yield_stmt: yield_expr;
raise_stmt: 'raise' (test ('from' test)?)?;
import_stmt: import_name | import_from;
import_name: 'import' dotted_as_names;

import_from: ('from' (('.' | '...')* dotted_name | ('.' | '...')+)
              'import' ('*' | '(' import_as_names ')' | import_as_names));
import_as_name: NAME ('as' NAME)?;
dotted_as_name: dotted_name ('as' NAME)?;
import_as_names: import_as_name (',' import_as_name)* (',')?;
dotted_as_names: dotted_as_name (',' dotted_as_name)*;
dotted_name: NAME ('.' NAME)*;
global_stmt: 'global' NAME (',' NAME)*;
nonlocal_stmt: 'nonlocal' NAME (',' NAME)*;
assert_stmt: 'assert' test (',' test)?;

compound_stmt: if_stmt | while_stmt | for_stmt | try_stmt | with_stmt | funcdef | classdef | decorated;
if_stmt: 'if' test ':' suite ('elif' test ':' suite)* ('else' ':' suite)?;
while_stmt: 'while' test ':' suite ('else' ':' suite)?;
for_stmt: 'for' exprlist 'in' testlist ':' suite ('else' ':' suite)?;
try_stmt: ('try' ':' suite
           ((except_clause ':' suite)+
            ('else' ':' suite)?
            ('finally' ':' suite)? |
           'finally' ':' suite));
with_stmt: 'with' with_item (',' with_item)*  ':' suite;
with_item: test ('as' expr)? ;
except_clause: 'except' (test ('as' NAME)?)?;


map_stmt : MAP_KEYWORD NAME '=' '{' (INDENT*(simple_stmt | compound_stmt)DEDENT*)+ '}' NEWLINE;
join_stmt :  (NAME '=')? (NAME '.')? JOIN_KEYWORD LB expr_stmt? RB SEMI_COLON NEWLINE;

test: or_test ('if' or_test 'else' test)? | lambdef |(QUOTES test QUOTES)  ;
test_nocond: or_test | lambdef_nocond;
lambdef: 'lambda' (varargslist)? ':' test;
lambdef_nocond: 'lambda' (varargslist)? ':' test_nocond;
or_test: and_test ('or' and_test)*;
and_test: not_test ('and' not_test)*;
not_test: 'not' not_test | comparison;
comparison: expr (comp_op expr)*;

comp_op: '<'|'>'|'=='|'>='|'<='|'<>'|'!='|'in'|'not' 'in'|'is'|'is' 'not';
star_expr: '*' expr;
expr: xor_expr ('|' xor_expr)*;
xor_expr: and_expr ('^' and_expr)*;
and_expr: shift_expr ('&' shift_expr)*;
shift_expr: arith_expr (('<<'|'>>') arith_expr)*;
arith_expr: term (('+'|'-') term)*;
term: factor (('*'|'/'|'%'|'//') factor)*;
factor: ('+'|'-'|'~') factor | power;
power: atom trailer* ('**' factor)?;
atom: ('(' (yield_expr|testlist_comp)? ')' |
       '[' (testlist_comp)? ']' |
       '{' (dictorsetmaker)? '}' |
       NAME | NUMBER | NAME+ | '...' | 'None' | 'True' | 'False' );
testlist_comp: (test|star_expr) ( comp_for | (',' (test|star_expr))* (',')? );
trailer: '(' (arglist)? ')' | '[' subscriptlist ']' | '.' NAME;
subscriptlist: subscript (',' subscript)* (',')?;
subscript: test | (test)? ':' (test)? (sliceop)?;
sliceop: ':' (test)?;
exprlist: (expr|star_expr) (',' (expr|star_expr))* (',')?;
testlist: test (',' test)* (',')?;
dictorsetmaker: ( (test ':' test (comp_for | (',' test ':' test)* (',')?)) |
                  (test (comp_for | (',' test)* (',')?)) );

classdef: 'class' NAME ('(' (arglist)? ')')? ':' suite;

arglist: (argument ',')* (argument (',')?
                         |'*' test (',' argument)* (',' '**' test)? 
                         |'**' test);

argument: test (comp_for)? | test '=' test;
comp_iter: comp_for | comp_if;
comp_for: 'for' exprlist 'in' or_test (comp_iter)?;
comp_if: 'if' test_nocond (comp_iter)?;


encoding_decl: NAME;

PROTOCOL_KEYWORD : 'Protocol';
SETUP_KEYWORD : 'Setup';
RUN_KEYWORD : 'Run';
MAP_KEYWORD : 'map';
JOIN_KEYWORD : 'join' | 'sum';
DATATYPE : SIMPLE_DATATYPE | VECTOR_DATATYPE | MATRIX_DATATYPE;
SIMPLE_DATATYPE : 'Val' | 'HVal' | 'GVal' | 'int' ;
VECTOR_DATATYPE : 'Vector' | 'HVector' | 'GVector';
MATRIX_DATATYPE : 'Matrix' | 'HMatrix' | 'GMatrix';
NUMBER : '0'..'9'+; 
NAME : (('a'..'z' | 'A'..'Z')+ (NUMBER|UNDER_SCORE)*)+;
//STRING : (('a'..'z' | 'A'..'Z')+ (NUMBER|UNDER_SCORE)*)+;

yield_expr: 'yield' (yield_arg)?;
yield_arg: 'from' test | testlist;
///End of Python Grammar 

//statement : assign_statement | protocol_invoc_statement | control_statement | method_invocation | map_statement | join_statement | fetchResult_statement //SEMI_COLON;

//name : simple_name | qualified_name ;
//simple_name: ID;
//qualified_name: name '.' ID;



//var_list : (ID COMMA)* ID;
//vector_var_list : var_list | ((ID LB (ID | INT)  RB COMMA)* (ID LB (ID | INT) RB));
//matrix_var_list : var_list | ((ID LB (ID | INT) COMMA (ID | INT) RB COMMA)* (ID LB (ID | INT) COMMA (ID | INT) RB));
//arg_list : (DATATYPE ID COMMA)* DATATYPE ID;

//PARTY : 'Client' | 'client' | 'Cloud' | 'cloud';


//INPUT_KEYWORD : 'Input' | 'input';
//OUTPUT_KEYWORD : 'Output' | 'output';


//ID : (('a'..'z' | 'A'..'Z')+ (INT|UNDER_SCORE)*)+;
//INT : '0'..'9'+; 
//COLON : ':';

NEWLINE : ('\r'?'\n' '\t'* ' '*);
WS		:   ( ' ' | '\t' )+ -> channel(HIDDEN);
COMMENT : '#' ~[\r\n]* {skip();};
//COMMENT  :	( ' ' | '\t' )* '#' ( ~'\n' )*   -> channel(HIDDEN);// let NEWLINE handle \n unless char pos==0 for '#' ;

//NEWLINE : '\r'?  '\n' ' '*;
LB : '(';
RB : ')';
//COMMA : ',';
UNDER_SCORE : '_';
SEMI_COLON : ';';
ENDMARKER : EOF;
QUOTES : '"';
//WS  :   (' '|'\t')+ {skip();} ;
